package infrastructure

import (
	"database/sql"
	"fmt"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"log"
	"time"
)

type Infrastructure struct {
	DbConn       *gorm.DB
	SqlDB        *sql.DB
	UserName     string
	Password     string
	ConnProtocol string
	DbIP         string
	DbPort       int
	DbName       string
	Charset      string
	ParseTime    string
	TimeZone     string
}

func (infs *Infrastructure) GetConn() *Infrastructure {
	user := "postgres"
	passswd := "yourpassword"
	ConnProtocol := "tcp"
	DbIP := "localhost"
	DbPort := 5432
	DbName := "gorm"
	Charset := "utf8"
	ParseTime := "true"
	TimeZone := "Asia/Shanghai"
	dsn := "host=localhost user=test password=Cmcc@123 dbname=test port=15432 sslmode=disable TimeZone=Asia/Shanghai"
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatal("连接数据库失败:", err)
	}
	log.Println("数据库连接成功")
	sqlDB, err := db.DB()
	if err != nil {
		log.Fatal("获取数据库实例失败:", err)
	}

	sqlDB.SetMaxIdleConns(10)
	sqlDB.SetMaxOpenConns(100)
	sqlDB.SetConnMaxLifetime(time.Hour)
	infstruct := &Infrastructure{
		DbConn:       db,
		SqlDB:        sqlDB,
		UserName:     user,
		Password:     passswd,
		ConnProtocol: ConnProtocol,
		DbIP:         DbIP,
		DbPort:       DbPort,
		DbName:       DbName,
		Charset:      Charset,
		ParseTime:    ParseTime,
		TimeZone:     TimeZone,
	}
	return infstruct
}

func (infs *Infrastructure) Exec(structural interface{}, batchSize ...int) error {
	var res *gorm.DB
	if len(batchSize) > 0 && batchSize[0] > 0 {
		res = infs.DbConn.CreateInBatches(structural, batchSize[0])
	} else {
		res = infs.DbConn.Create(structural)
	}
	if res.Error != nil {
		return res.Error
	}
	return res.Error
}

func (infs *Infrastructure) SaveOrUpdate(model interface{}, conflictColumns []string, updateColumns []string) error {
	res := infs.DbConn.Clauses(clause.OnConflict{
		Columns:   toClauseColumns(conflictColumns),
		DoUpdates: clause.AssignmentColumns(updateColumns),
	}).Create(model)

	return res.Error
}

func (infs *Infrastructure) SaveOrUpdateDynamic(model interface{}, conflictColumns []string, updateData map[string]interface{}) error {
	res := infs.DbConn.Clauses(clause.OnConflict{
		Columns:   toClauseColumns(conflictColumns),
		DoUpdates: clause.Assignments(updateData),
	}).Create(model)

	return res.Error
}

func toClauseColumns(cols []string) []clause.Column {
	var result []clause.Column
	for _, c := range cols {
		result = append(result, clause.Column{Name: c})
	}
	return result
}

func (infs *Infrastructure) Query(sqlStr string) *sql.Rows {
	// 执行查询并扫描结果
	rows, _ := infs.DbConn.Raw(sqlStr).Rows()
	//defer func(rows *sql.Rows) {
	//	err := rows.Close()
	//	if err != nil {
	//		fmt.Errorf("fetch rows field")
	//	}
	//}(rows)
	return rows

}

func (infs *Infrastructure) Close() error {
	err := infs.SqlDB.Close()
	if err != nil {
		return err
	}
	return nil
}

func (infs *Infrastructure) AutoMigrate(dest ...interface{}) {
	if len(dest) == 0 {
		fmt.Errorf("至少需要提供一个模型")
	}
	err := infs.DbConn.AutoMigrate(dest...)
	if err != nil {
		log.Fatal("自动迁移失败:", err)
	}
	log.Println("表结构迁移完成")
}
